/*
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *
 *   版权所有  2014-2015 成都星锐蓝海网络科技有限公司
 *   商业许可请联系  +86-18682011860    QQ:66442834
 *   
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "xyz_log.h"
#include "xyz_curl.h"


static size_t xyz_curl_write_data_to_mem(void *ptr, size_t size, size_t nmemb, void *arg);
static size_t xyz_curl_write_data_to_file(void *ptr, size_t size, size_t nmemb, void *arg);
static size_t xyz_curl_write_data_to_stream(void *ptr, size_t size, size_t nmemb, void *arg);
static size_t xyz_curl_write_data_to_fno(void *ptr, size_t size, size_t nmemb, void *arg);


static size_t xyz_curl_write_data_to_mem(void *ptr, size_t size, size_t nmemb, void *arg)
{
	char *tmp = NULL;
	char **p = (char **)arg;
	int len = 0;

	len = (size * nmemb);
	if (len < 1) {
		*p = NULL;
		return 0;
	}
	tmp = (char *)calloc(nmemb, size);
	if (!tmp) {
		xyz_log_error("calloc : %s", strerror(errno));
		return -1;
	}
	if (!memcpy(tmp, ptr, len)) {
		xyz_log_error("memcpy : %s", strerror(errno));
		free(tmp);
	}
	*p = tmp;
	return (size * nmemb);
}

static size_t xyz_curl_write_data_to_file(void *ptr, size_t size, size_t nmemb, void *arg)
{
	xyz_curl_file_t *dwnfile = (xyz_curl_file_t *)arg;
	if (!dwnfile || !dwnfile->filename) {
		return -1;
	}
	if (!dwnfile->stream) {
		dwnfile->stream = fopen(dwnfile->filename, "wb");
		if (!dwnfile->stream) {
			return -1;
		}
	}
	size_t rc = fwrite(ptr, size, nmemb, dwnfile->stream);
	if (rc < 0) {
		rc = -1;
	}
	return rc;
}

static size_t xyz_curl_write_data_to_stream(void *ptr, size_t size, size_t nmemb, void *arg)
{
	FILE *fp = (FILE *)arg;
	size_t rc = fwrite(ptr, size, nmemb, fp);
	if (rc < 0) {
		rc = -1;
	}
	return rc;
}

static size_t xyz_curl_write_data_to_fno(void *ptr, size_t size, size_t nmemb, void *arg)
{
	unsigned long int fd = (unsigned long int)arg;
	size_t rc = write(fd, ptr, (size * nmemb));
	if (rc < 0) {
		rc = -1;
	}
	return rc;
}

CURL *xyz_curl_easy_init(void)
{
	CURLcode crc;
	CURL *ch;

	xyz_log_debug("libcurl version %s", curl_version());
	crc = curl_global_init(CURL_GLOBAL_ALL);
	if (CURLE_OK != crc) {
		xyz_log_error("init libcurl failed.");
		return NULL;
	}
	ch = curl_easy_init();
	if (!ch) {
		xyz_log_error("get a CURL easy handle failed.");
		curl_global_cleanup();
		return NULL;
	}
	curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L);
	curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT, 60L);
	curl_easy_setopt(ch, CURLOPT_TIMEOUT, 360L);
	curl_easy_setopt(ch, CURLOPT_VERBOSE, 0L);
	curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1L);
	return ch;
}


void xyz_curl_easy_close(CURL *ch)
{
	curl_easy_cleanup(ch);
	curl_global_cleanup();
}

int xyz_curl_easy_GET_to_mem(CURL *ch, const char *url, void **out, double *size)
{
	CURLcode crc = CURLE_OK;
	if (!ch || !url || !out || !size ) {
		return -1;
	}
	curl_easy_setopt(ch, CURLOPT_URL, url);
	curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, xyz_curl_write_data_to_mem);
	curl_easy_setopt(ch, CURLOPT_WRITEDATA, out);

	crc = curl_easy_perform(ch);
	if (crc != CURLE_OK) {
		xyz_log_error("libcurl error: [%d]%s", crc, curl_easy_strerror(crc));
		if (*out) {
			free(*out);
		}
		*size = 0.0;
		return -1;
	}
	curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, size);
	return 0;
}

int xyz_curl_easy_GET_to_file(CURL *ch, const char *url, const char *file, double *size)
{
	CURLcode crc = CURLE_OK;
	xyz_curl_file_t dwnfile;
	if (!ch || !url || !file || !size ) {
		return -1;
	}
	dwnfile.filename = file;
	dwnfile.stream = NULL;
	curl_easy_setopt(ch, CURLOPT_URL, url);
	curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, xyz_curl_write_data_to_file);
	curl_easy_setopt(ch, CURLOPT_WRITEDATA, &dwnfile);

	crc = curl_easy_perform(ch);
	if (crc != CURLE_OK) {
		xyz_log_error("libcurl error: [%d]%s", crc, curl_easy_strerror(crc));
		*size = 0.0;
		if (dwnfile.stream) {
			fclose(dwnfile.stream);
		}
		return -1;
	}
	curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, size);
	if (dwnfile.stream) {
		fclose(dwnfile.stream);
	}
	return 0;
}

int xyz_curl_easy_GET_to_stream(CURL *ch, const char *url, FILE *fp, double *size)
{
	CURLcode crc = CURLE_OK;
	if (!ch || !url || !fp || !size ) {
		return -1;
	}
	curl_easy_setopt(ch, CURLOPT_URL, url);
	curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, xyz_curl_write_data_to_stream);
	curl_easy_setopt(ch, CURLOPT_WRITEDATA, fp);

	crc = curl_easy_perform(ch);
	if (crc != CURLE_OK) {
		xyz_log_error("libcurl error: %s", curl_easy_strerror(crc));
		*size = 0.0;
		return -1;
	}
	curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, size);
	return 0;
}

int xyz_curl_easy_GET_to_fno(CURL *ch, const char *url, int fd, double *size)
{
	CURLcode crc = CURLE_OK;
	if (!ch || !url || fd < 0 || !size ) {
		return -1;
	}
	curl_easy_setopt(ch, CURLOPT_URL, url);
	curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, xyz_curl_write_data_to_fno);
	curl_easy_setopt(ch, CURLOPT_WRITEDATA, fd);

	crc = curl_easy_perform(ch);
	if (crc != CURLE_OK) {
		xyz_log_error("libcurl error: [%d]%s", crc, curl_easy_strerror(crc));
		*size = 0.0;
		return -1;
	}
	curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, size);
	return 0;
}
